#ifdef _WIN32
#include <windows.h>
#endif

#include "GLee.h"
#include <GL/glu.h>

#include "Blade.h"
#include "glslshader.h"
#include "Scene.h"

using namespace std;

int Blade::BUFFER_CAPACITY = 20;
GLSLProgram* Blade::m_shaderProgram = NULL;

Blade::Blade(boost::shared_ptr<Scene> scene)
: Entity(scene)
{
    m_bladePoints.set_capacity(BUFFER_CAPACITY);
}

Blade::~Blade(void)
{
}

bool Blade::isBladed(const Vector3& position, const Dimension& size) const
{
    bool found = false;

    if (!m_bladePoints.empty())
    {
        // Check if any of the points stored into the circular buffer lies inside the
        // rectangle defined by the position and the size indicated as input parameters
        boost::circular_buffer<std::pair<int,int>>::const_iterator it;
        for (it=m_bladePoints.begin() ; it!=m_bladePoints.end(); it++)
        {
            int x = (*it).first;
            int y = (*it).second;
            int left = static_cast<int> (position.x - (size.width / 2.0f));
            if ((x >= left) && (x <= (left + size.width)))
            {
                int top = static_cast<int> (position.y + (size.height / 2.0f));
                if ((y >= (top - size.height)) && (y <= top))
                {
                    found = true;
                    break;
                }
            }
        } // for
    } // if (!m_bladePoints.empty())

    return found;
}

bool Blade::onInitialize()
{
    vector<GLfloat> vertex;
    vertex.assign(BUFFER_CAPACITY * 2, 0.0f);

    glGenBuffers(1, &m_vertexBuffer); //Generate a buffer for the vertices
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); //Bind the vertex buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * BUFFER_CAPACITY * 2, &vertex[0], GL_STATIC_DRAW); //Send the data to OpenGL

    vector<GLfloat> color;
    for (int i=0; i<(BUFFER_CAPACITY * 2); i++)
    {
        color.push_back(1.0f);
        color.push_back(0.0f);
        color.push_back(0.0f);
        color.push_back(1.0f);
    }

    glGenBuffers(1, &m_colorBuffer); //Generate a buffer for the colors
    glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); //Bind the color buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * BUFFER_CAPACITY * 4, &color[0], GL_STATIC_DRAW); //Send the data to OpenGL

    if (m_shaderProgram == NULL)
    {
        m_shaderProgram = new GLSLProgram("data/shaders/basic-RGBAcolor.vert", 
                                          "data/shaders/basic-RGBAcolor.frag");

        if (!m_shaderProgram->initialize())
        {
            std::cerr << "Could not initialize the Pause Screen shaders" << std::endl;
            return false;
        }

        m_shaderProgram->bindAttrib(0, "a_Vertex");
        m_shaderProgram->bindAttrib(1, "a_Color");
        m_shaderProgram->linkProgram();
    }

    return true;
}

void Blade::onPrepare(float dT)
{
    if (m_scene->isButtonHold(0))
    {
        int mousePositionX;
        int mousePositionY;
        m_scene->getMousePos(mousePositionX, mousePositionY);
        
        pair<int,int> newPoint = make_pair(mousePositionX, mousePositionY);
        m_bladePoints.push_back(newPoint);
    }

    if (m_scene->hasButtonBeenReleased(0))
    {
        m_bladePoints.clear();
    }
}

void Blade::onRender() const
{
    if(!m_bladePoints.empty())
    {
        static float modelviewMatrix[16];
        static float projectionMatrix[16];

        glDisable(GL_CULL_FACE);
        glDisable(GL_DEPTH_TEST);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glPushMatrix();
        glTranslatef(0.0f, 0.0f, 0.0f); 

        glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
        glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);

        m_shaderProgram->bindShader();
        m_shaderProgram->sendUniform4x4("modelview_matrix", modelviewMatrix);
        m_shaderProgram->sendUniform4x4("projection_matrix", projectionMatrix);

        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
        vector<GLfloat> vertex = vectorize();
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * vertex.size(), &vertex[0]);
        glVertexAttribPointer((GLint)0, 2, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);
        vector<GLfloat> color;
        for (unsigned int i=0; i<vertex.size(); i++)
        {
            color.push_back(1.0f);
            color.push_back(0.0f);
            color.push_back(0.0f);
            color.push_back(1.0f);
        }
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * color.size(), &color[0]);
        glVertexAttribPointer((GLint)1, 4, GL_FLOAT, GL_FALSE, 0, 0);

        glLineWidth(5.0f);
        glDrawArrays(GL_LINE_STRIP, 0, m_bladePoints.size());

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);

        glPopMatrix();

        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
        glDisable(GL_BLEND);
    }
}

void Blade::onPostRender()
{
}

void Blade::onShutdown()
{
}

void Blade::onDestroy()
{
}

vector<GLfloat> Blade::vectorize(void) const
{
    vector<GLfloat> result;
    boost::circular_buffer<std::pair<int,int>>::const_iterator it;
    for (it=m_bladePoints.begin() ; it!=m_bladePoints.end(); it++)
    {
        result.push_back(static_cast<GLfloat> ((*it).first));
        result.push_back(static_cast<GLfloat> ((*it).second));
    }

    return result;
}

